﻿using System;

namespace TestByWire.Tutorial.Domain
{
    public class SalaryManager
    {
        private readonly IEmployeStorage _storage;
        private readonly ISalaryIndex _salaryIndex;
        private readonly ITaxationCalulator _taxCalculator;

        public SalaryManager(IEmployeStorage storage, ISalaryIndex salaryIndex, ITaxationCalulator taxCalculator, IEventRaiser eventRaiser)
        {
            _storage = storage;
            _salaryIndex = salaryIndex;
            _taxCalculator = taxCalculator;
            eventRaiser.eventToRaise += OnUpdate;
        }

        private void OnUpdate(object sender, StringEventArgs e)
        {
            RaiseSalary(e == null ? null : e.Name);
        }

        public double RaiseSalary(String employeeId)
        {
            EmployeInformation emplInfo = _storage.RetrieveCurrentSalary(employeeId);
            ISalaryStrategy salStrategy = _salaryIndex.GetSalaryStrategy(emplInfo.Title);
            double saleryAdjustment = salStrategy.CalculateSalaryAdjustment(emplInfo.Salary);
            double tax = _taxCalculator.CalculateTax(saleryAdjustment);
            double newSalery = emplInfo.Salary + saleryAdjustment + tax;
            _storage.UpdateSalary(emplInfo, newSalery);
            return newSalery;
        }

        public double[] GetSaleries(params String[] employes)
        {
            var outSal = new double[employes.Length];
            for (int i = 0; i < employes.Length; i++)
            {
                outSal[i] = _storage.RetrieveCurrentSalary(employes[i]).Salary;
            }

            return outSal;
        }

    }

    public class StringEventArgs : EventArgs
    {
        public string Name { get; set; }

        public StringEventArgs(String name)
        {
            Name = name;
        }
    }

    public interface IEventRaiser
    {
        event EventHandler<StringEventArgs> eventToRaise;
    }

    public struct EmployeInformation
    {
        public string Title { get; set; }

        public double Salary { get; set; }

        public bool Equals(EmployeInformation other)
        {
            return Equals(other.Title, Title) && other.Salary.Equals(Salary);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (obj.GetType() != typeof(EmployeInformation)) return false;
            return Equals((EmployeInformation)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((Title != null ? Title.GetHashCode() : 0) * 397) ^ Salary.GetHashCode();
            }
        }
    }

    public interface ITaxationCalulator
    {
        double CalculateTax(double saleryAdjustment);
    }

    public interface ISalaryIndex
    {
        ISalaryStrategy GetSalaryStrategy(string employeeId);
    }

    public interface ISalaryStrategy
    {
        double CalculateSalaryAdjustment(double currentSalery);
    }

    public interface IEmployeStorage
    {
        EmployeInformation RetrieveCurrentSalary(string employeeId);
        void UpdateSalary(EmployeInformation employeInformation, double newSalary);
    }
}
